﻿define([
    'dojo/_base/declare',
    'dojo/_base/array',
    'dojo/_base/connect',
    'dojo/dom-construct',
    'dojo/dom-style',
    'esri/domUtils',
    'esri/lang',
    'esri/layers/layer',
    'esri/geometry/Point',
    'geomap/plugins/Div.js',
], function (
    declare,
    ArrayUtils,
    ConnectUtils,
    DomConstructUtils,
    DomStyleUtils,
    DomUtils,
    esriLang,
    Layer,
    Point,
    Div
) {
    var DivsLayer = declare([Layer], {
        constructor: function (options) {
            //  this.inherited(arguments, [options]);
            this._options = options;
            this._map = null; //地图对象
            this._element = null; //图层dom元素
            this._connects = []; //事件集合
            this.graphics = [];

            this.loaded = true;
            this.onLoad(this);
        },

        _setMap: function (map, layersElement) {
            //map.addLayer会触发
            var me = this;
            this._map = map;

            map._divLayers = map._divLayers || {};

            this._element = DomConstructUtils.create(
                'div',
                {
                    style: 'position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px;height: 0px; width: 0px;',
                },
                layersElement
            );
            var thisElement = this._element;
            map._divLayers[me.id] = { layerIndex: me._options.layerIndex, _element: thisElement };
            this._layersElement = layersElement;
            /*加载新的divLayer，map会自动对图层重新排序，应对这个bug,需要再遍历所有已上图的divLayers,将他们添加到父节点上*/
            // map._divLayersHandle = setTimeout(function () {
            //     var keys = Object.keys(map._divLayers) || [];
            //     keys.map(function (key) {
            //         var item = map._divLayers[key];
            //         if (item.layerIndex == undefined) {
            //             layersElement.parentNode.appendChild(item._element);
            //         }
            //         ;
            //     })
            // }, 1000);
            this._connects.push(
                ConnectUtils.connect(map, 'onLayerAdd', this, this._layerAddHandler)
            );
            this._connects.push(
                ConnectUtils.connect(map, 'onLayerRemove', this, this._layerRemoveHandler)
            );
            this._connects.push(
                ConnectUtils.connect(
                    this,
                    'onVisibilityChange',
                    this,
                    this._visibilityChangeHandler
                )
            );
            this._connects.push(ConnectUtils.connect(map, 'onPan', this, this._panHandler));
            this._connects.push(ConnectUtils.connect(map, 'onPanEnd', this, this._panEndHandler));
            this._connects.push(
                ConnectUtils.connect(map, 'onZoomStart', this, this._zoomStartHandler)
            );
            this._connects.push(ConnectUtils.connect(map, 'onZoomEnd', this, this._zoomEndHandler));
            this._connects.push(
                ConnectUtils.connect(map, 'onExtentChange', this, this._extentChangeHandler)
            );

            return this._element;
        },
        _unsetMap: function (map, layersElement) {
            //map.removeLayer会触发
            //console.log("_unsetMap");
            ArrayUtils.forEach(this._connects, ConnectUtils.disconnect, this);
            if (this._element) {
                //layersElement.removeChild(this._element);
                this._element.parentNode.removeChild(this._element);
            }
            delete map._divLayers[this.id];

            this._map = null;
            this._element = null;
            this._connects = null;
            this.graphics = null;
        },
        _layerAddHandler: function () {
            var map = this._map;
            if (!map) {
                return;
            }
            var _layersElement = this._layersElement;
            var keys = Object.keys(map._divLayers) || [];
            keys.map(function (key) {
                var item = map._divLayers[key];
                if (item.layerIndex == undefined) {
                    _layersElement.parentNode.appendChild(item._element);
                }
            });
        },
        _layerRemoveHandler: function () {
            this._layerAddHandler();
        },
        _visibilityChangeHandler: function (visible) {
            if (visible) {
                DomUtils.show(this._element);
            } else {
                DomUtils.hide(this._element);
            }
        },

        _panHandler: function (delta, extent) {
            //平移时计算移动坐标差
            DomStyleUtils.set(this._element, {
                transform: 'translate(' + extent.x + 'px, ' + extent.y + 'px)',
            });
        },
        _panEndHandler: function (delta, extent) {
            //平移结束恢复坐标
            DomStyleUtils.set(this._element, {
                transform: 'translate(0px, 0px)',
            });
        },

        _zoomStartHandler: function (anchor, extent, level, zoomFactor) {
            DomUtils.hide(this._element);
        },
        _zoomEndHandler: function (anchor, extent, level, zoomFactor) {
            var map = this.getMap();
            this.setScale(map, this._element);
        },

        _extentChangeHandler: function (extent, delta) {
            this._refresh();
        },

        add: function (div) {
            this.graphics.push(div);

            this._draw(div);
        },

        remove: function (graphic) {
            var b = this.graphics;
            var d, k;
            var m = {
                indexOf: fn(!0),
                lastIndexOf: fn(!1),
            };
            if (-1 === (d = m.indexOf(b, graphic))) return null;

            var g = b.length;
            if (g) {
                for (; d < g; ) (b[d] = b[d + 1]), d++;
                b.length--;
            }

            this._removeShape(graphic, null, null);
            graphic._shape = graphic._graphicsLayer = null;
            graphic._element.remove();

            // this.onGraphicRemove(graphic);
            return graphic;

            function fn(a) {
                var b = 1,
                    d = 0,
                    c = 0;
                a || (b = d = c = -1);
                return function (e, f, g, h) {
                    if (h && 0 < b) return m.lastIndexOf(e, f, g);
                    h = (e && e.length) || 0;
                    var q = a ? h + c : d;
                    g === k
                        ? (g = a ? d : h + c)
                        : 0 > g
                        ? ((g = h + g), 0 > g && (g = d))
                        : (g = g >= h ? h + c : g);
                    for (h && 'string' == typeof e && (e = e.split('')); g != q; g += b)
                        if (e[g] == f) return g;
                    return -1;
                };
            }
        },
        addAll: function (divs) {
            for (var i = 0; i < divs.length; i++) {
                this.add(divs[i]);
            }
        },

        _draw: function (div) {
            var map = this.getMap();
            if (map == null) {
                return;
            }
            if (div._element == null) {
                return;
            }
            div._element.style.visibility = 'hidden';
            this._element.appendChild(div._element);

            var width = div._element.offsetWidth;
            var height = div._element.offsetHeight;

            if (div._element.__remove) {
                div._element.parentNode.removeChild(div._element);
                return;
            }
            div._element.style.visibility = 'visible';

            var minPoint = new Point(map.extent.xmin, map.extent.ymin, map.spatialReference);
            var maxPoint = new Point(map.extent.xmax, map.extent.ymax, map.spatialReference);
            var screenMinPoint = map.toScreen(minPoint);
            var screenMaxPoint = map.toScreen(maxPoint);

            var x = div.geometry.x;
            var y = div.geometry.y;

            var left = ((x - minPoint.x) / (maxPoint.x - minPoint.x)) * screenMaxPoint.x;
            var top = (1 - (y - minPoint.y) / (maxPoint.y - minPoint.y)) * screenMinPoint.y;

            // var marginLeft = 0 - div._element.offsetWidth / 2;
            // var marginTop = 0 - div._element.offsetHeight / 2;
            if (this.symbolOptions) {
                var offsety = this.symbolOptions.offsety || 0;
                var offsetx = this.symbolOptions.offsetx || 0;
                var position = this.symbolOptions.position || 'top';
                switch (position) {
                    case 'top':
                        left = left - width / 2 - offsetx;
                        top = top - height - offsety;
                        break;
                    case 'right':
                        left = left + offsetx;
                        top = top - offsety;
                        break;
                    case 'bottom':
                        left = left - width / 2 - offsetx;
                        top = top + height + offsety;
                        break;
                    case 'left':
                        left = left - width - offsetx;
                        top = top - offsety;
                        break;
                }
            } else {
                left = left - width / 2;
                top = top - height / 2;
            }
            DomStyleUtils.set(div._element, {
                left: left + 'px',
                top: top + 'px',
            });
        },
        setScale: function (map, _element) {
            var scale = map.getScale();
            var isHide = false;
            if (this.options && this.options.minScale > 0 && this.options.maxScale > 0) {
                if (scale > this.options.minScale || scale < this.options.maxScale) {
                    DomUtils.hide(_element);
                    isHide = true;
                } else {
                    DomUtils.show(_element);
                }
            } else if (this.options && this.options.minScale > 0) {
                if (scale > this.options.minScale) {
                    DomUtils.hide(_element);
                    isHide = true;
                } else {
                    DomUtils.show(_element);
                }
            } else if (this.options && this.options.maxScale > 0) {
                if (scale < this.options.maxScale) {
                    DomUtils.hide(_element);
                    isHide = true;
                } else {
                    DomUtils.show(_element);
                }
            } else {
                DomUtils.show(_element);
            }
            return isHide;
        },
        clear: function () {
            //console.log("clear");
            var childs = this._element.childNodes;
            for (var i = childs.length - 1; i >= 0; i--) {
                this._element.removeChild(childs[i]);
            }
            this.graphics = [];
        },
        _refresh: function () {
            //console.log("_refresh");
            var map = this.getMap();
            if (map == null) {
                return;
            }
            var div;
            for (var i = 0; i < this.graphics.length; i++) {
                div = this.graphics[i];
                if (
                    map.extent.xmin <= div.geometry.x &&
                    div.geometry.x <= map.extent.xmax &&
                    map.extent.ymin <= div.geometry.y &&
                    div.geometry.y <= map.extent.ymax
                ) {
                    this.onGraphicDraw({
                        graphic: {
                            _element: div._element,
                        },
                    });
                } else {
                    this._removeShape({
                        _element: div._element,
                    });
                }
                this._draw(div);
            }
        },

        onGraphicDraw: function (event) {
            event.graphic._element.__remove = false;
        },
        _removeShape: function (graphic) {
            graphic._element.__remove = true;
        },

        on: function (type, fn) {
            var me = this;

            type = type.toLocaleLowerCase();
            ConnectUtils.connect(this._element, type, this, function (e) {
                var graphicElement = e.target;
                while (
                    graphicElement.parentNode != me._element &&
                    graphicElement.parentNode != null
                ) {
                    graphicElement = graphicElement.parentNode;
                }
                if (graphicElement.graphic) {
                    var event = {
                        graphic: graphicElement.graphic,
                    };
                    fn(event);
                }
            });
        },
        convertSr: function (mapView, lngLatArray, callback) {
            var me = this;
            if (!mapView) {
                return;
            }
            var mapUtils = mapView.mapUtils;
            var mapSr = mapView.getSpatialReference();
            me.mapSr = mapSr;
            var layerSr = me.spatialReference;
            /*坐标转换，扩展的div不像svg可以自动转换坐标，如果点位的坐标与底图的坐标不一致需要先手工转换坐标*/
            if (mapSr.wkid == '102100') {
                if (layerSr.wkid == '4326' || layerSr.wkid == '4490') {
                    me.lngLatToXY(mapView, mapUtils, lngLatArray, function (lngLatArray) {
                        if ($.isFunction(callback)) {
                            callback(lngLatArray, mapSr);
                        }
                    });
                    return;
                }
            }
            if (mapSr.wkid == '4326' || mapSr.wkid == '4490') {
                if (layerSr.wkid == '102100') {
                    me.xyToLngLat(mapView, mapUtils, lngLatArray, function (lngLatArray) {
                        if ($.isFunction(callback)) {
                            callback(lngLatArray, mapSr);
                        }
                    });
                    return;
                }
            }
            if ($.isFunction(callback)) {
                callback(lngLatArray, me.spatialReference);
            }
        },
        lngLatToXY: function (mapView, mapUtils, lngLatArray, callback) {
            mapUtils.webMercatorUtils(mapView, lngLatArray, { isLngLat: true, callback: callback });
        },
        xyToLngLat: function (mapView, mapUtils, data, callback) {
            mapUtils.webMercatorUtils(mapView, lngLatArray, {
                isLngLat: false,
                callback: callback,
            });
        },
    });
    return DivsLayer;
});
